home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d1
/
multicol.arc
/
MC.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-06-19
|
11KB
|
465 lines
/*
* m c . c
*
* Multi-column filter
*
*/
/*)BUILD
$(TKBOPTIONS) = {
TASK = ...MCX
UNITS = 10
ACTFIL = 10
}
*/
/*
)EDITLEVEL=40
* Edit history
* 0.0 ??-???-?? ??? Original implementation distributed with DECUS C.
* 1.0 19-May-81 JSL Extensive reorganization; added -t option, balancing
* of columns on final page. Bugfix: Don't put out a
* blank initial item.
* 2.0 20-Jul-82 JSL Converted to tool standards.
* 2.1 23-Jul-82 JSL Added -g switch.
* 2.2 27-Jul-82 JSL Added multiple-file handling.
* 2.3 1-Aug-82 JSL Redid overstrike handling; much more extensive error
* and bounds checks; debug conditionally compiled.
* 2.4 ??-Aug-82 MM Change default page height to 60
* 2.5 23-Sep-82 JSL Added conditional code for VAX-11 C
* 2.6 11-apr-85 SCP Ported to ms-DOS DESMET 'C' removed piping
* capability for performance, removed space between opt and #
*/
char *documentation[] = {
" mc [-ccolumns] [-hheight] [-ggutter] [-wwidth]",
" [filespec | aliasspec]... outfile",
"",
"mc reads one or more files and converts them to a single multi-column file",
"that it writes to an output file. ",
"",
"Alias specifications provide a method for controlling the placement of file",
"data. An alias specification is a reference to another file (or alias)",
"specification. File and alias specifications are numbered, starting at one",
"for the left-most such specification; switches and their arguments do not",
"affect the numbering. The alias specification #n indicates that the n'th",
"specification is to be repeated. Such a specification is legal only if it",
"refers to an earlier specification.",
"",
"mc has two modes of operation:",
" one input file - the file is presented in multicolumn format,",
" records may be truncated to fit.",
" multiple inputs - each input file (or alais) will occupy one column ",
" on the output. Files should be of equal length.",
"",
"The following switches are available:",
" -c Number of columns default - 2",
" -h Height, in lines, of a page default - 58",
" -g Gutter width (the space between columns) default - 1",
" -w Width, in characters, of a page default - 80",
"",
" mc [-c2] [-h58] [-g1] [-w80]",
" [filespec | aliasspec]... outfile",
0 };
#ifdef vax11c
#include ctype.h
#include stdio.h
#define exits(x) exit(x)
#define IO_ERROR 2
#define FALSE 0
#define TRUE 1
#define EOS 0
#else
#include <stdio.h>
#endif
/*
* Turn on to include debugging code
*/
/* #define DEBUG */
#define LINEMAX 256 /* Maximum line length handled */
/* (also maximum column width) */
#define NFILES 10 /* Maximum files (including */
/* aliased files) */
#define ALIAS '#' /* Marks an alias argument */
/* (Can't be "-") */
#ifdef DEBUG
int debug = 0;
#endif
int NEWLN = 3338;
int columns = -1; /* All these will be given */
int gutter = -1; /* default values later unless */
int height = -1; /* the user sets them first */
int width = -1; /* (to a positive value!) */
int pause = FALSE; /* Pause-at-end of page flag */
int first = TRUE; /* First-time-through flag */
int cwidth; /* Total (column+gutter) width */
int pagesize; /* Total bytes in page */
int nf; /* Number of file & alias specs */
int files = 0; /* Number of files still open */
int aliases = 0; /* Number of alias specs */
FILE file[NFILES]; /* File pointers for our files */
FILE outfp; /* SP - and rightmost is output file */
char line[LINEMAX]; /* Input line buffer */
int linelen; /* Length of a line in line[] */
int lineend; /* Last usable line[] position */
char *page; /* -> page paste-up matrix */
main(argc,argv)
int argc;
char *argv[];
{ register char *p;
register int c,i;
int n;
long PAGE;
FILE fp;
if (argc < 2) usage();
if (argc == 2 && argv[1][0] == '?' && strlen(argv[1]) == 1)
{ help();
return;
}
--argc; /* SP---*/
nf = argc - 1;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') break;
--nf;
c = argv[i][1];
switch(tolower(c))
{
#ifdef DEBUG
case 'd': debug++; break;
#endif
case 'c':
columns = atoi(&argv[i][2]);
if (columns <= 0) bad("Columns");
break;
case 'g':
gutter = atoi(&argv[i][2]);
if (gutter <= 0) bad("Gutter");
break;
case 'h':
height = atoi(&argv[i][2]);
if (height <= 0) bad("Height");
break;
case 'w':
width = atoi(&argv[i][2]);
if (width <= 0) bad("Width");
break;
default:
usage();
break;
}
}
if (nf == 0)
{
error("Too few files");
}
else
for ( ; i < argc ; i++)
if (p = argv[i])
switch (*p)
{
case ALIAS:
n = atoi(&p[1]) - 1;
if (n < 0 || n >= files) usage("File");
file[files++] = file[n];
aliases++;
break;
default:
if ((fp = fopen(p,"r")) == NULL)
{ fprintf(stderr,
"%s: ",p);
error("Can't open");
}
#ifdef DEBUG
fprintf(stderr,"\nOpened input: %s",p);
#endif
file[files++] = fp;
break;
}
if (nf > NFILES )
error("Too many file and alias arguments, or bad parm order");
if ((outfp = fopen(argv[argc],"w")) == NULL)
{ fprintf(stderr, "%s: ",argv[i]);
error("Can't open"); }
#ifdef DEBUG
fprintf(stderr,"\nOpened output: %s",argv[i]);
#endif
files -= aliases; /* Aliases aren't open */
/*
* Establish defaults for any parameters the user didn't set
*/
if (width < 0) width = 80;
if (gutter < 0) gutter = 1;
if (height < 0) height = 58;
if (columns < 0)
if (nf > 1)
columns = nf;
else
columns = 2;
/*
* The last column isn't followed by a gutter, but dealing with this makes
* the computation too complex; so we simply pretend the page is wider, which
* is ok since the code trims the trailing spaces that would go there anyway.
* This is, of course, quite wasteful of space, but then so is the whole algo-
* rithm; we shouldn't be storing ANY of the gutters explicitly.
*/
width += gutter;
cwidth = width/columns;
if (cwidth <= gutter || (cwidth - gutter) > LINEMAX)
error("Unreasonable -c/-g/-w combination\n");
lineend = line + (cwidth - gutter);
PAGE = ( long) height * (width + gutter);
if (PAGE > 30000L)
error("Insufficient memory - sorry\n");
page = calloc(pagesize = (int) PAGE);
if (page == NULL )
error("Insufficient memory - sorry\n");
#ifdef DEBUG
if (debug)
{ fprintf(stderr,"width %d, height %d, columns %d, cwidth %d\n",
width,height,columns,cwidth);
fprintf(stderr,
"\tgutter %d, pause %d, pagesize %d, page at 0%o\n",
gutter,pause,pagesize,page);
fprintf(stderr,"%d files(%d real + %d aliases)\n",
nf,files,aliases); }
#endif
process();
fclose(outfp);
free(page);
}
/*
* Process all the data
*/
process()
{ register int offset; /* Offset into page */
register int items; /* Counts items added */
register int maxitems; /* Room for this many */
int curfile; /* Current file */
maxitems = columns * height;
fputc('\f',outfp);
blank();
curfile = items = offset = 0;
while (get(file[curfile]))
{ if (items >= maxitems)
{ output(items);
blank();
items = offset = 0;
}
#ifdef DEBUG
if (debug > 3)
fprintf(stderr,"Inserting %s at offset %d, file %d\n",
line,offset,curfile);
#endif
strncpy(page+offset,line,linelen);
items++;
if ((items % height) == 0) /* Bottom of a column */
{ curfile = (curfile + 1) % nf;
#ifdef DEBUG
if (debug > 1)
fprintf(stderr,"Switching to file %d of %d\n",
curfile,files);
#endif
}
offset += cwidth;
}
output(items);
}
/*
* Print out the buffered page, which has been filled with items items.
*/
output(items)
int items; /* # of items the caller used */
{ int nrows;
register int i,col,row;
#ifdef DEBUG
if (debug) fprintf(stderr,"output items(%d), nf(%d)\n",items,nf);
#endif
if (items <= 0) /* Nothin' to do */
return;
/*
* Get number of rows we'll need. This is the basis of the "last page"
* optimization - we don't use all the rows, just enough to hold everything
* (items/columns, rounded up). If there's more than are one file, just use
* the whole page.
*/
if (nf == 1) nrows = (items + (columns - 1)) / columns;
else nrows = height;
#ifdef DEBUG
if (debug > 1)
fprintf(stderr,"items %d, nrows %d\n",items,nrows);
if (debug > 2)
{ page[pagesize] = 0;
fprintf(stderr,"Dump of page:\n%s\n",page);
}
#endif
if (first)
first = FALSE;
else
{ if (pause)
{ printf("\t\t\t Type CTRL/Z to exit, any other key to continue...");
fflush(stdout);
i = ci();
fwrite(&NEWLN,1,2,outfp);
if (i == 26) /* CTRL/Z */
exit();
}
fputc('\f',outfp);
}
/*
* Scan through page[] row-wise, after having filled it column-wise. (Page[]
* is laid out column-wise in memory.)
*/
for (row = 0; row < nrows; row++)
{ for (col = 0; col < columns; col++)
putitem(page+(row+col*nrows)*cwidth,
(col == columns - 1));
fwrite(&NEWLN,1,2,outfp);
}
}
/*
* Put out one item, possibly trimming trailing spaces
*/
putitem(base,trim)
register char *base; /* First char to put */
int trim; /* Trim trailing spaces */
{ register char *end; /* End of item */
int len;
end = &base[cwidth - 1];
if (trim)
while (*end == ' ')
--end;
len = end - base;
#ifdef DEBUG
if (len > 256)
puts("\n wow -excessive length");
#endif
if (len > 0)
fwrite(base,1,len + 1,outfp);
}
/* * Blank out page[] */
blank()
{ register int n;
for (n = 0; n < pagesize;)
page[n++] = ' ';
}
/* * Fill line[]; return FALSE when all files have reached EOF, TRUE until then.
*/
get(fp)
FILE fp;
{ register char *p; /* Current char pos */
register char *high; /* Char pos high water */
register int c; /* Character */
char tempc;
if ((tempc = getc(fp)) == -1)
{ linelen = 0; /* Pretend we read "" */
return(TRUE);
}
ungetc(tempc,fp);
high = p = line;
while ((c = getc(fp)) != EOF && c != '\n')
switch(c)
{
case '\b':
if (p > line)
--p;
break;
case '\r':
p = line;
break;
case '\t':
if (((p - line) & 07) != 07)
ungetc(c,fp);
c = ' ';
/*
* Fall through...
*/
default:
if (isprint(c))
{ if ((p < lineend)
&& (p == high || *p == '_' || *p == ' '))
{ *p = c;
if (p == high)
high++;
}
p++;
}
break;
}
linelen = high - line;
if (c != EOF)
return(TRUE);
else
return((--files != 0));
}
bad(s)
char *s;
{ printf("\n %s specification bad",s); usage(); }
usage()
{
fprintf(stderr,"\nUsage:\n mc [-ccolumns] [-ggutter] ");
puts("[-hheight] [-wwidth] [infile(s) | #n]... outfile\n");
error("\n mc ? for more help");
}
help()
/*
* Give good help
*/
{ register char **dp;
for (dp = documentation; *dp; dp++)
printf("%s\n",*dp);
}
error(str)
char *str;
{ puts(str);
exit(1);
}